package com.mike.filter;

import com.mike.bo.MonitorInfo;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * @Description 利用TPMonitorFilter在消费端记录每个方法的请求耗时时间（异步调用不进行记录）
 * 每隔5s打印一次最近1分钟内每个方法的TP90、TP99的耗时情况
 * @Date 2021/11/16 10:56
 * @Created by mike
 */
//在消费者端进行激活此拦截器
@Activate(group = {CommonConstants.CONSUMER})
public class TPMonitorFilter implements Filter, Runnable {

    private final double PERCENT90 = 0.9;  //90分位
    private final double PERCENT99 = 0.99; //99分位

    private Map<String, List<MonitorInfo>> monitorInfoMap = new ConcurrentHashMap<>();
//    private Map<String, List<MonitorInfo>> monitorInfoMap = new HashMap<>();

    /**
     * 方法执行就拦截：利用TPMonitorFilter在消费端记录每个方法的请求耗时时间
     */
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {

        String methodName = invocation.getMethodName();
        long startTime = System.currentTimeMillis();
        Result result = invoker.invoke(invocation);
        long totalTime = System.currentTimeMillis() - startTime;
        List<MonitorInfo> monitorInfoList = monitorInfoMap.get(methodName);
        if (monitorInfoList==null){
            monitorInfoList = new ArrayList<>();
            monitorInfoMap.put(methodName,monitorInfoList);
        }
        monitorInfoList.add(new MonitorInfo(totalTime, startTime));
        return result;
    }

    public TPMonitorFilter() {
        // 创建定时线程，每隔5s打印一次最近1分钟内每个方法的TP90、TP99的耗时情况
        Executors.newSingleThreadScheduledExecutor()
                .scheduleWithFixedDelay(this, 5, 5, TimeUnit.SECONDS);
    }

    @Override
    public void run() {
        long currentTime = System.currentTimeMillis();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        String dateStr = sdf.format(new Date(currentTime));
        System.out.println("-----------------------------------------");
        /**
         * 线程使用情况
         */
        monitorInfoMap.forEach((methodName, monitorInfoList) -> {
            System.out.println(monitorInfoList.size());
            monitorInfoList.removeIf(monitorInfo -> monitorInfo.getStartTime() < (currentTime - 1000 * 60));
            Map<Double, Long> result = getTP90AndTP99(monitorInfoList, currentTime);
            System.out.println(dateStr + methodName + "的TP90:" + result.get(PERCENT90) + "毫秒,"
                    + "TP99:" + result.get(PERCENT99) + "毫秒");
        });
    }

    private Map<Double, Long> getTP90AndTP99(List<MonitorInfo> monitorInfoList, long currentTime) {
        HashMap<Double, Long> result = new HashMap<>();
        List<MonitorInfo> statics = new ArrayList<>();
        int monitorInfoListSize = monitorInfoList.size();
        for (int i = 0; i < monitorInfoListSize; i++) {
            MonitorInfo monitorInfo = monitorInfoList.get(i);
            long startTime = monitorInfo.getStartTime();
            if (startTime >= (currentTime - 60000) && startTime < currentTime){
                statics.add(monitorInfo);
            }
        }
        statics.sort((o1, o2) -> (int) (o1.getTotalTime() - o2.getTotalTime()));
        int size = statics.size();
        result.put(PERCENT90, statics.get((int) (size * PERCENT90)).getTotalTime());
        result.put(PERCENT99, statics.get((int) (size * PERCENT99)).getTotalTime());
        return result;
    }
}
